package ldh.im.web.rest;

import ldh.common.spring.security.cryptogram.RsaUtil;
import ldh.im.business.pojo.ImUser;
import ldh.im.business.pojo.Tenant;
import ldh.im.web.ResponseResult;
import ldh.im.web.config.AesService;
import ldh.im.web.config.RsaService;
import ldh.im.web.dto.FxRefreshToken;
import ldh.im.web.dto.FxLoginUserDto;
import ldh.im.web.service.ImUserService;
import ldh.im.web.service.TenantService;
import ldh.im.web.util.JsonUtil;
import ldh.im.web.util.ShaUtil;
import ldh.im.web.validate.ParamCheckUtil;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.Date;
import java.util.UUID;

@RestController
@RequestMapping("/client")
public class RestLoginController {

    @Resource
    private RsaService rsaService;
    @Resource
    private TenantService tenantService;
    @Resource
    private ImUserService imUserService;
    @Resource
    private AesService aesService;
    @Value("${im.loginErrorNum}")
    private Integer loginErrorNum;
    @Value("${im.salt}")
    private String salt;

    @PostMapping(path="/imLogin")
    public ResponseResult login(@RequestParam("data") String encodeData, @RequestParam("imCode") String encodeImCode) throws Exception {
        ParamCheckUtil.notEmpty(encodeData, "数据不能为空");
        ParamCheckUtil.notEmpty(encodeImCode, "im编码不能为空");

        String imCode = rsaService.decryptByPrivateKey(encodeImCode);

        Tenant tenant = tenantService.getByCode(imCode);
        ParamCheckUtil.notNull(tenant, "im编码错误");

        String data = RsaUtil.decryptByPublicKey(encodeData, tenant.getPublicKey2());
        FxLoginUserDto userDto = JsonUtil.toObject(data, FxLoginUserDto.class);

        String encodePassword = ShaUtil.sign(userDto.getPassword());

        ImUser imUser = imUserService.getByCodeAndTenantId(userDto.getName(), tenant.getId());
        ParamCheckUtil.notNull(imUser, "登录信息错误");
        checkLock(imUser);
        if (!imUser.getPassword().equals(encodePassword)) { // 登录失败
            loginError(imUser);
        }
        ParamCheckUtil.isTrue(imUser.getPassword().equals(encodePassword), "登录信息错误");

        String loginToken = UUID.randomUUID().toString();
        String encodeLoginToken = ShaUtil.sign(loginToken);

        String freshToken = createFreshToken(imUser);
        handleUserLoginSuccess(imUser, freshToken);

        return ResponseResult.build().put("token", encodeLoginToken).put("refreshToken", freshToken);
    }

    @PostMapping(path="/token/refresh")
    public ResponseResult refreshToken(@RequestParam("data") String encodeData) throws Exception {
        ParamCheckUtil.notEmpty(encodeData, "数据不能为空");

        String data = aesService.decrypt(encodeData);

        FxRefreshToken fxRefreshToken = JsonUtil.toObject(data, FxRefreshToken.class);
        ParamCheckUtil.notNull(fxRefreshToken, "refreshToken is error");

        checkRefreshToken(fxRefreshToken);

        ImUser imUser = imUserService.getByIdAndTenantId(fxRefreshToken.getUserId(), fxRefreshToken.getTenantId());
        ParamCheckUtil.notNull(imUser, "refreshToken错误");

        String loginToken = UUID.randomUUID().toString();
        String encodeLoginToken = ShaUtil.sign(loginToken);

        String freshToken = createFreshToken(imUser);

        return ResponseResult.build().put("token", encodeLoginToken).put("refreshToken", freshToken);
    }

    private void loginError(ImUser imUser) {
        int loginErrorNum = imUser.getLoginErrorNum() == null ? 1 : imUser.getLoginErrorNum() + 1;
        imUser.setLoginErrorNum(loginErrorNum);
        imUser.setUpdateTime(new Date());
        if (loginErrorNum > this.loginErrorNum) {
            imUser.setLockTime(DateUtils.addHours(new Date(), 2));
        }
        imUserService.updateById(imUser);
    }

    private void checkLock(ImUser imUser) {
        if (imUser.getLockTime() != null) {
            ParamCheckUtil.isTrue(imUser.getLockTime().before(new Date()), "账户被锁定");
        }
        if (imUser.getLoginErrorNum() != null) {
            ParamCheckUtil.isTrue(imUser.getLoginErrorNum() < loginErrorNum, "登录失败次数过多，被锁定");
        }
    }

    private void handleUserLoginSuccess(ImUser imUser, String freshToken) {
        imUser.setLockTime(null);
        imUser.setLoginErrorNum(0);
        imUser.setLoginTime(new Date());
        imUser.setRefreshToken(freshToken);
        imUser.setUpdateTime(new Date());
        imUserService.updateById(imUser);
    }

    private String createFreshToken(ImUser imUser) throws Exception {
        FxRefreshToken fxRefreshToken = new FxRefreshToken();
        fxRefreshToken.setUserId(imUser.getId());
        fxRefreshToken.setExpireDate(DateUtils.addDays(new Date(), 4));
        fxRefreshToken.setTenantId(imUser.getTenantId());

        String tokenJson = JsonUtil.toJson(fxRefreshToken);
        fxRefreshToken.setSignToken(ShaUtil.sign(tokenJson + "-" + salt));
        String json = JsonUtil.toJson(fxRefreshToken);
        String freshToken = aesService.encrypt(json);

        return freshToken;
    }

    private void checkRefreshToken(FxRefreshToken fxRefreshToken) throws Exception {
        String signToken = fxRefreshToken.getSignToken();
        fxRefreshToken.setSignToken(null);
        String tokenJson = JsonUtil.toJson(fxRefreshToken);
        fxRefreshToken.setSignToken(signToken);
        String newSignToken = ShaUtil.sign(tokenJson + "-" + salt);
        ParamCheckUtil.isTrue(signToken.endsWith(newSignToken), "refreshToken错误");
        ParamCheckUtil.isTrue(fxRefreshToken.getExpireDate().after(new Date()), "refresh已经过期");
    }
}
